package org.king.service.impl;

import jakarta.annotation.Resource;
import org.king.pojo.dto.account.ResetPasswordConfirmDTO;
import org.king.pojo.dto.account.ResetPasswordDTO;
import org.king.pojo.entity.Account;
import org.king.pojo.dto.account.AccountRegisterByEmailDTO;
import org.king.mapper.AccountMapper;
import org.king.service.AccountService;
import org.king.constant.KeyConst;
import org.king.util.FlowUtil;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeUnit;

@Service
public class AccountServiceImpl implements AccountService {
    @Resource
    private FlowUtil flowUtil;
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private AccountMapper accountMapper;
    @Resource
    private AmqpTemplate amqpTemplate;

//    @Resource
//    private MailUtils mailUtils;
    @Resource
    private PasswordEncoder passwordEncoder;

    @Override
    public List<Account> getAllUsers() {
        return accountMapper.getAllUsers();
    }

    @Override
    public Account getUserById(int id) {
        return accountMapper.getUserById(id);
    }

    /**
     * 通过用户名或者邮箱查询用户
     * @param text 用户名或者邮箱
     * @return 用户信息
     */
    @Override
    public Account getUserByEmailOrUsername(String text) throws UsernameNotFoundException  {
        return accountMapper.getUserByEmailOrUsername(text);
    }

    /**
     * 生成注册验证码存入Redis中，并将邮件发送请求提交到消息队列等待发送
     * @param type 类型
     * @param email 邮件地址
     * @param address 请求IP地址
     * @return 操作结果，null表示正常，否则为错误原因
     */
    public String registerEmailVerifyCode(String type, String email, String address){
        synchronized (address.intern()) {
            if(!this.verifyLimit(address))
                return "请求频繁，请稍后再试";
            Random random = new Random();
            int code = random.nextInt(899999) + 100000;
            Map<String, Object> data = Map.of("type",type,"email", email, "code", code);
            amqpTemplate.convertAndSend(KeyConst.MQ_MAIL, data);
//            mailUtil.sendMailMessage(data);
            stringRedisTemplate.opsForValue() //为验证码设置三分钟有效时间
                    .set(KeyConst.VERIFY_EMAIL_DATA + email, String.valueOf(code), 3, TimeUnit.MINUTES);
            return null;
        }
    }

    /**
     * 注册账号
     * @param vo 请求参数，请求传输过来的参数
     * @return 是否成功吧
     */
    @Override
    public String registerEmailAccount(AccountRegisterByEmailDTO vo) {
        String email = vo.getEmail();
        String username = vo.getUsername();
        String code = stringRedisTemplate.opsForValue().get(KeyConst.VERIFY_EMAIL_DATA + email);
        if(code == null){
            return "please get code";
        }
        else if(!code.equals(vo.getCode())){
            return "验证码输入错误";
        }
        else if(isExistEmail(email)){
            return "邮箱已经被注册了";
        }
        else if(isExistUsername(username)){
            return "该用户名已经被注册了";
        }
        else {
            String password = passwordEncoder.encode(vo.getPassword());
            int number = accountMapper.insertUser(username, password, email);
            if(number == 0){
                return "database error";
            }else {
                stringRedisTemplate.delete(KeyConst.VERIFY_EMAIL_DATA + email);
                return null;
            }
        }
    }

    @Override
    public String resetConfirmCode(ResetPasswordConfirmDTO dto) {
        String email = dto.getEmail();
        String code = dto.getCode();

        String codeCorrect = stringRedisTemplate.opsForValue().get(KeyConst.VERIFY_EMAIL_DATA + email);
        if(code == null){ return "please get code"; }
        else if(!code.equals(codeCorrect)){ return "验证码有误，请重新输入"; }
        return null;
    }

    @Override
    public String resetPassword(ResetPasswordDTO dto) {
        String email = dto.getEmail();
        String code = dto.getCode();
        String password = dto.getPassword();

        String flag = resetConfirmCode(new ResetPasswordConfirmDTO(email,code));
        if(flag != null){ return flag; }
        int update = accountMapper.updatePassword(passwordEncoder.encode(password),email);
        if(update == 0){
            return "database error";
        }
        stringRedisTemplate.delete(KeyConst.VERIFY_EMAIL_DATA + email);
        return null;
    }

    private Boolean isExistEmail(String email){
        return accountMapper.getUserByEmail(email) != null;
    }
    private Boolean isExistUsername(String username){
        return accountMapper.getUserByUsername(username) != null;
    }

    private boolean verifyLimit(String address) {
        String key = KeyConst.VERIFY_EMAIL_LIMIT + address;
        return flowUtil.limitOnceCheck(key,60);
    }
}
